package com.nuo.laboratory.juc.util;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import lombok.Builder;
import lombok.Data;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author LGC
 * @date 2022/4/6 15:40
 */
public class ThreadLocalTest {


    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 100,
            1, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1024), ThreadFactoryBuilder.create().setNamePrefix("test").build());

    /**
     * ThreadLocal 每个线程的变量副本
     * ThreadLocalMap Thread的一个变量,key = TreadLocal的弱引用 value = 变量副本
     * ThreadLocal 发生内存泄露主要原因，ThreadLocalMap与Thread的生命周期一样长，只要当前线程没有结束，key就不会释放
     * 解决方式
     * 1每次用完ThreadLocal手动 remove key
     * 2将ThreadLocal 变量定义为 private static 就一直存在ThreadLocal强引用，
     * 也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值，进而清除掉
     */
    public static void main(String[] args) {
        // simpleDateFormat 多个线程副本
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    SimpleDateFormat simpleDateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
                    System.out.println(simpleDateFormat.format(new Date()));
                }
            });
        }
        // 避免每个方法参数传递
        UserHolder.set(LoginInfo.builder().id(1).username("张三").build());
        new Server2().process();
        threadPoolExecutor.shutdown();
    }
}


class ThreadSafeFormatter {

    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new
            ThreadLocal<SimpleDateFormat>() {
                @Override
                protected SimpleDateFormat initialValue() {
                    return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS");
                }
            };
}


class Server2 {
    public void process() {
        System.out.println(UserHolder.get().toString());
    }
}

class UserHolder {
    private static ThreadLocal<LoginInfo> HOLDER = new ThreadLocal<>();

    public static LoginInfo get() {
        return HOLDER.get();
    }

    public static void set(LoginInfo loginInfo) {
        HOLDER.set(loginInfo);
    }

    public static void remover() {
        HOLDER.remove();
    }
}

@Data
@Builder
class LoginInfo {
    Integer id;
    String username;
}

